/**
 * Elite trclink2 CTI Client
 * Created by Jeff on 2020/09/01
 */

(function (factory) {
	"use strict";
	if (typeof define === 'function' && define.amd) {
		define(factory);
	} else {
		window.cticlient = factory();
	}
}(function () {
	//debugger;
	var MSGTYPE = $CTI_CONST.CTI_MSG;

	function addJsonStringValue(jsonobj, name, value) {
		jsonobj[name.toLowerCase()] = { type: 3, value: value };
	}

	function addJsonIntValue(jsonobj, name, value) {
		jsonobj[name.toLowerCase()] = { type: 1, value: value };
	}

	function addJsonDateValue(jsonobj, name, value) {
		jsonobj[name.toLowerCase()] = { type: 4, value: value };
	}

	function addJsonDatasValue(jsonobj, name, value) {
		jsonobj[name.toLowerCase()] = { type: 5, value: value };
	}

	function getJsonString(jsonobj, name) {
		if (jsonobj.hasOwnProperty(name)) {
			return jsonobj[name];
		}
		return "";
	}

	function isJson(content) {
		try {
			var jsonStr = JSON.parse(content);
			return true;
		} catch (e) {
			return false;
		}
	}

	function isArray(object) {
		return object && typeof object === 'object' &&
			typeof object.length === 'number' &&
			typeof object.splice === 'function' &&
			//for arary, return false  
			!(object.propertyIsEnumerable('length'));
	}

	function getdatabykey(datas, key) {
		var d = datas;
		try {
			if (typeof (d) == 'string') {
				d = JSON.parse(d);
			}
			if (d) {
				for (var vkey in d) {
					if (vkey.toLowerCase() == key.toLowerCase()) {
						if (typeof (d) == 'object') {
							if (d.hasOwnProperty('value')) {
								return d[vkey].value;
							} else {
								return d[vkey];
							}
						} else {
							return d[vkey];
						}
					}
				}
			}
		} catch (err) {

		}
	}

	function tosimpleuserdata(datas) {
		//convert to JSON object. special treatment for raw_data uu_data.
		try {
			if (typeof (datas) == 'string') {
				datas = JSON.parse(datas);
			}
			var uui = getdatabykey(datas, 'uu_data');
			if (uui) {
				return { 'uui': uui };
			} else {
				var uui = getdatabykey(datas, 'raw_data');
			}
			if (uui) {
				try {
					uui = JSON.parse(uui);
				} catch (err) {

				}
				return uui;
			} else {
				//return datas;
				var uui = '';
				var v;
				for (var p in datas) {
					if (datas[p].hasOwnProperty('value')) {
						v = tosimpledata(datas[p].value);
					} else {
						v = datas[p];
					}
					uui = uui + '|' + p + '=' + v;
				}
				uui = uui.substring(1);
				return { 'uui': uui };
			}
		} catch (err) {
			return { 'uui': datas };
		}
	}

	function tosimpledata(data) {
		try {
			if (typeof (data) == 'object') {
				var jdata = {};
				for (var p in data) {
					if (data[p].hasOwnProperty('value')) {
						jdata[p] = tosimpledata(data[p].value);
					} else {
						jdata[p] = data[p];
					}
				}
				return jdata;
			} else {
				return data;
			}
		} catch (err) {
			return data;
		}
	}

	function commonreplace(template, args) {
		if (arguments.length > 1 && typeof arguments[0] === "string") {
			var result = arguments[0];
			if (arguments.length == 2 && typeof arguments[1] === "object") {
				var obj = arguments[1];
				for (var key in obj) {
					if (obj[key] != undefined) {
						var reg = new RegExp("({" + key + "})", "g");
						result = result.replace(reg, obj[key]);
					}
				}
			} else {
				for (var i = 1, len = arguments.length; i < len; i++) {
					if (arguments[i] != undefined) {
						// may cause error when > 9
						// var reg = new RegExp("({[" + (i - 1) + "]})", "g");
						var reg = new RegExp("({)" + (i - 1) + "(})", "g");
						result = result.replace(reg, arguments[i]);
					}
				}
			}
			return result;
		} else {
			return arguments[0];
		}
	}

	function arrayfind(arr, findstr, col, colret, nofoundret) {
		if (isArray(arr) && findstr) {
			for (let i = 0; i < arr.length; i++) {
				if (arr[i][col] == findstr) {
					return arr[i][colret];
				}
			}
		}
		return nofoundret;
	}

	var Connection = {
		STATE_IDLE: 0,
		STATE_RINGING: 1,
		STATE_DIALING: 2,
		STATE_TALKING: 3,
		STATE_HELD: 4,
		createNew: function () {
			var connection = {};
			connection.state = Connection.STATE_UNKONW;
			connection.connectionid = "";
			return connection;
		}
	};

	var CallInfo = {
		CALLTYPE_UNKNOWN: 0,
		CALLTYPE_INBOUND: 1,
		CALLTYPE_OUTBOUND: 2,
		createNew: function () {
			var callinfo = {};
			callinfo.activecall = null;
			callinfo.heldcall = null;
			callinfo.calltype = CallInfo.CALLTYPE_UNKNOWN;
			callinfo.tpreleased = 0;
			callinfo.starttime = null;
			callinfo.establishtime = null;
			callinfo.endtime = null;
			return callinfo;
		}
	};

	var AGENTWORKMODE = {
		AUTOIN: 1,
		MANUALIN: 2,
		AUX: 3,
		ACW: 4
	};

	var EliteCallbackCode = {
		wsclose: 100,
		wsopen: 101,
		agentstatus: 1,
		agentcallstatus: 2,
		trunkstatus: 3,
		extstatus: 4,
		ibfromotheragent: 5,
		ibfromoutside: 6
	};

	var ELITETRClink2 = {
		createNew: function () {
			var connectionid1 = "conn-1";
			var connectionid2 = "conn-2";

			var softphone = {};

			softphone.dn = "";
			softphone.agentid = "";
			softphone.agentpassword = "";
			softphone.agentgroup = "";
			softphone.callinfo = CallInfo.createNew();

			Config = TRClink2Config;

			function logmsg(msg, jsonobj) {
				var msg1;
				if (jsonobj) {
					msg1 = "[trclink2]" + msg + ": " + JSON.stringify(jsonobj)
				} else {
					msg1 = "[trclink2]" + msg;
				}
				$SC.addconsolelog(msg1);
			};

			function logret(callname, strret) {
				logmsg("return " + callname + " " + strret);
			};

			function logreq(callname) {
				var s = "request " + callname + "(";
				for (let i = 1; i < arguments.length; i++) {
					var s1 = arguments[i];
					if (typeof (s1) === "object") {
						s1 = JSON.stringify(s1);
					}
					if (i === 1) {
						s = s + s1;
					} else {
						s = s + "," + s1;
					}
				}
				s = s + ")";
				logmsg(s);
			};

			function returnbyresult(strret) {
				return (strret === "OK")
			};

			softphone.OnCTIEvent = function (evt) {
				var servertime = $SC.getservertime();
				if (!evt.servertime) {
					addJsonDateValue(evt, "servertime", $SC.getservertime());
				}
				logmsg("[OnCTIEvent]:" + JSON.stringify(evt));
				this.OnMessage(evt);
			};

			softphone.InternalOnError = function (errMessage, errorCode, errorName) {
				var ret = {};
				ret.event = { type: 1, value: $CTI_CONST.CTI_MSG.EVT_ERROR };
				ret.dn = { type: 3, value: this.dn };
				//ret.channelid = this.channelid;
				if (!errorCode) {
					errorCode = 80001
				}
				ret.errorcode = { type: 3, value: errorCode };
				ret.errordesc = { type: 3, value: errMessage };
				if (errorName) {
					ret.errorname = { type: 3, value: errorName };
				}
				softphone.OnCTIEvent(JSON.stringify(ret));
			};

			softphone.InternalOnErrorCode = function (errorCode, errorMsg) {
				var ret = {};
				ret.event = { type: 1, value: $CTI_CONST.CTI_MSG.EVT_ERROR };
				ret.dn = { type: 3, value: this.dn };
				//ret.channelid = this.channelid;
				if (!errorCode) {
					errorCode = 80001
				}
				ret.errorcode = { type: 3, value: errorCode };
				ret.errordesc = { type: 3, value: errorMsg };
				softphone.OnCTIEvent(JSON.stringify(ret));
			};

			softphone.keepalive = function () {

			};

			//action
			softphone.connectserver = function (serverendpoint, onOpen, onClose, onMessage, onError, onSendMessage) {
				//debugger;
				softphone.OnSendMessage = onSendMessage;
				softphone.OnMessage = onMessage;
				softphone.OnError = onError;

				var evt = {};
				addJsonIntValue(evt, "event", MSGTYPE.EVT_LINKCONNCTED);
				addJsonStringValue(evt, "eventdesc", "EVT_LINKCONNECTED");
				softphone.OnCTIEvent(evt);

				var auxcodes = $SC.executeselectsql("softphone.auxcode.query.v201610", []);
				if (auxcodes && auxcodes.value) {
					softphone.auxcodes = auxcodes.value[0].fields;
				} else {
					softphone.auxcodes = [];
				}
			};

			softphone.registerdn = function (dn) {
				this.dn = dn;
				this.channelid = 1;
				//在registerdn中做setup，可以根据传入的dn和正则表达式来判断是否使用sipphone
				ClinkAgent.setup({ sipPhone: Config.siphoneregex.test(dn), debug: Config.trdebug }, function () {
					//监听所有事件
					ClinkAgent.registerListener(ClinkAgent.EventType.STATUS, function (event) {
						softphone.OnAgentStatusEvent(event);
					}); //坐席状态事件
					ClinkAgent.registerListener(ClinkAgent.EventType.PREVIEW_OUTCALL_START, function (event) {
						softphone.OnPreviewOutCallStart(event);
					}); // 预览外呼呼叫客户
					ClinkAgent.registerListener(ClinkAgent.EventType.PREVIEW_OUTCALL_RINGING, function (event) {
						softphone.OnPreviewOutCallRinging(event);
					}); // 预览外呼客户响铃
					ClinkAgent.registerListener(ClinkAgent.EventType.PREVIEW_OUTCALL_BRIDGE, function (event) {
						softphone.OnPreviewOutCallBridge(event);
					}); // 预览外呼客户接听
					ClinkAgent.registerListener(ClinkAgent.EventType.CONSULT_START, function (event) {
						softphone.OnConsultStart(event);
					}); // 咨询开始
					ClinkAgent.registerListener(ClinkAgent.EventType.CONSULT_LINK, function (event) {
						softphone.OnConsultLink(event);
					}); // 咨询接听
					ClinkAgent.registerListener(ClinkAgent.EventType.CONSULT_ERROR, function (event) {
						softphone.OnConsultError(event);
					}); // 咨询失败
					ClinkAgent.registerListener(ClinkAgent.EventType.UNCONSULT, function (event) {
						softphone.OnUnconsult(event);
					}); // 咨询接回/挂断/取消
					ClinkAgent.registerListener(ClinkAgent.EventType.CONSULT_THREEWAY, function (event) {
						softphone.OnConsultThreeway(event);
					}); // 咨询三方
					ClinkAgent.registerListener(ClinkAgent.EventType.CONSULT_THREEWAY_UNLINK, function (event) {
						softphone.OnConsultThreewayUnlink(event);
					}); // 咨询三方挂断
					ClinkAgent.registerListener(ClinkAgent.EventType.CONSULT_TRANSFER, function (event) {
						softphone.OnConsultTransfer(event);
					}); // 咨询转移
					ClinkAgent.registerListener(ClinkAgent.EventType.RINGING, function (event) {
						softphone.OnRinging(event);
					}); // 弹屏、响铃
					ClinkAgent.registerListener(ClinkAgent.EventType.PREVIEW_OUTCALL, function (event) {
						softphone.OnPreviewOutCall(event);
					}); // 预览外呼
					ClinkAgent.registerListener(ClinkAgent.EventType.KICKOUT, function (event) {
						softphone.OnKickout(event);
					}); // 被踢下线
					ClinkAgent.registerListener(ClinkAgent.EventType.BREAK_LINE, function (event) {
						softphone.OnBreakLine(event);
					}); // 断线
					//对所有操作的回调做捕获错误的处理
					ClinkAgent.registerCallback(ClinkAgent.ResponseType.LOGIN, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.LOGIN, event); }); // 登录

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.LOGOUT, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.LOGOUT, event); }); //退出

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.PAUSE, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.PAUSE, event); }); // 置忙

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.UNPAUSE, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.UNPAUSE, event); }); // 置闲

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.PREVIEW_OUTCALL, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.PREVIEW_OUTCALL, event); }); // 预览外呼

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.PREVIEW_OUTCALL_CANCEL, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.PREVIEW_OUTCALL_CANCEL, event); }); // 外呼取消

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.REFUSE, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.REFUSE, event); }); // 拒接

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.UNLINK, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.UNLINK, event); }); // 挂断

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.HOLD, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.HOLD, event); }); // 保持

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.UNHOLD, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.UNHOLD, event); }); // 保持接回

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.MUTE, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.MUTE, event); }); // 静音

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.UNMUTE, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.UNMUTE, event); }); // 取消静音

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.CONSULT, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.CONSULT, event); }); // 咨询

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.CONSULT_CANCEL, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.CONSULT_CANCEL, event); }); // 咨询取消

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.CONSULT_TRANSFER, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.CONSULT_TRANSFER, event); }); // 咨询转移

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.UNCONSULT, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.UNCONSULT, event); }); // 咨询接回

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.TRANSFER, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.TRANSFER, event); }); // 转移

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.INTERACT, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.INTERACT, event); }); // 交互

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.INVESTIGATION, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.INVESTIGATION, event); }); // 满意度调查

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.DTMF, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.DTMF, event); }); // 发送按键

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.CHANGE_BIND_TEL, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.CHANGE_BIND_TEL, event); }); // 修改绑定电话

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.PROLONG_WRAPUP, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.PROLONG_WRAPUP, event); }); // 延长整理时间

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.SIP_LINK, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.SIP_LINK, event); }); // 软电话接听

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.SIP_UNLINK, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.SIP_UNLINK, event); }); // 软电话挂断

					ClinkAgent.registerCallback(ClinkAgent.ResponseType.SPY, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.SPY, event); }); // 监听
					ClinkAgent.registerCallback(ClinkAgent.ResponseType.BARGE, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.BARGE, event); }); // 强插
					ClinkAgent.registerCallback(ClinkAgent.ResponseType.DISCONNECT, function (event) { softphone.OnCTICallBack(ClinkAgent.ResponseType.DISCONNECT, event); }); // 强拆


					//看上去不能马上在此之后直接调用login，但是又没有消息，只能等待一段时间
					console.log('等待1秒再发送registered消息');
					setTimeout(function () {
						var ret = {};
						addJsonIntValue(ret, "dn", this.dn);
						addJsonIntValue(ret, "channelid", this.channelid);
						addJsonIntValue(ret, "event", MSGTYPE.EVT_REGISTERED);
						addJsonStringValue(ret, "eventdesc", "EVT_REGISTERED");
						softphone.OnCTIEvent(ret);
						return true;
					}, 1000);
				});
			};

			softphone.login = function (agentid, dn, agentpassword, agentgroup, workmode, acdagent) {
				this.agentid = agentid;
				this.agentpassword = agentpassword;
				this.agentgroup = agentgroup;

				this.agentid = agentid;
				this.agentpassword = agentpassword;
				this.agentgroup = agentgroup;

				var params = {};
				params.identifier = $SC._getconfig("TRClink2Config", "identifier") || Config.identifier;
				params.cno = agentid;
				params.password = agentpassword;
				params.bindTel = this.dn;
				if (Config.siphoneregex && Config.siphoneregex.test(this.dn)) {
					params.bindType = 3; //siphone
				} else if (Config.extphoneregex.test(this.dn)) {
					params.bindType = 2; //普通分机
				}
				else {
					params.bindType = 1; //外线电话
				}
				softphone.bindType = params.bindType;
				params.loginStatus = 2; //登录后为置忙状态

				logreq("ClinkAgent.login", params);
				ClinkAgent.login(params);
			};

			softphone.agentlogout = function () {
				var params = {
					logoutMode: 1, //退出方式，0：退出后台在线、1：完全退出
					removeBinding: 1 //解绑电话，0：不解绑、1：解绑
				};
				logreq("ClinkAgent.logout", params);
				ClinkAgent.logout(params);
			};

			softphone.agentnotready = function (workmode) {
				if (workmode == 4) {
					return softphone.acw();
				}
				return softphone.aux(0);
			};

			softphone.aux = function (reasoncode) {
				var params = {};
				params.pauseType = 1; //类型，1：置忙、2：休息 怀疑休息是否要收到平台约束，一律用置忙
				var pausedesc = "";
				if (reasoncode > 0) {
					pausedesc = arrayfind(softphone.auxcodes, reasoncode, 0, 1);
				}
				if (pausedesc) {
					params.pauseDescription = pausedesc;
				}
				logreq("ClinkAgent.pause", params);
				ClinkAgent.pause(params);
			};

			softphone.acw = function () {
				logmsg("unsupported request acw");
				return false;
			};

			softphone.agentready = function (workmode) {
				var params = {};
				logreq("ClinkAgent.unpause", params);
				ClinkAgent.unpause(params);
			};

			softphone.makecall = function (dest, ani) {
				var params = {};
				params.tel = dest;
				logreq("ClinkAgent.previewOutcall", params);
				ClinkAgent.previewOutcall(params);
			};

			softphone.releasecall = function () {
				var params = {};
				if (this.callinfo.calltype == CallInfo.CALLTYPE_OUTBOUND) {
					if (this.callinfo.activecall.state == CallInfo.STATE_DIALING) {
						logreq("ClinkAgent.previewOutcallCancel", params);
						ClinkAgent.previewOutcallCancel(params);
					} else {
						logreq("ClinkAgent.unlink", params);
						ClinkAgent.unlink(params);
					}

				} else {
					logreq("ClinkAgent.unlink", params);
					ClinkAgent.unlink(params);
				}
			}

			softphone.holdcall = function () {
				var params = {};
				logreq("ClinkAgent.hold", params);
				ClinkAgent.hold(params);
			};

			softphone.answercall = function () {
				if (softphone.bindType === 3) {
					logreq("ClinkAgent.sipLink");
					ClinkAgent.sipLink();
				} else {
					logmsg("unsupported request answercall");
				}
			};

			softphone.retrievecall = function (callid) {
				var params = {};
				logreq("ClinkAgent.unhold", params);
				ClinkAgent.unhold(params);
			};

			softphone.inittransfer = function (callid, dest, userdatas) {
				var params = {};
				//咨询类型，0：电话号码，1：座席号，2：分机号
				if (Config.agentidregex.test(dest)) {
					params.objectType = 1;
				} else if (Config.siphoneregex.test(dest) || Config.extphoneregex.test(dest)) {
					params.objectType = 2;
				} else {
					params.objectType = 0;
				}
				params.consultObject = dest;
				logreq("ClinkAgent.consult", params);
				ClinkAgent.consult(params);
			};

			softphone.initconference = function (callid, dest, userdatas) {
				return softphone.inittransfer(callid, dest, userdatas);
			};

			softphone.reconnect = function () {
				var params = {};
				logreq("ClinkAgent.consultCancel", params);
				ClinkAgent.consultCancel(params);
			};

			softphone.completetransfer = function () {
				var params = {};
				logreq("ClinkAgent.consultTransfer", params);
				ClinkAgent.consultTransfer(params);
			};

			softphone.completeconference = function () {
				var params = {};
				logreq("ClinkAgent.consultThreeway", params);
				ClinkAgent.consultThreeway(params);
			};

			softphone.mutetransfer = function (callid, dest, userdatas) {
				if (dest == 'investigation') {
					logreq("ClinkAgent.investigation", params);
					ClinkAgent.investigation(params);
				} else {
					var params = {};
					//转移类型，0：电话号码、1：座席号、2：分机号、3：语音导航节点、4：语音导航id
					//ivr转接根据datas里的数据去判断
					if (Config.agentidregex.test(dest)) {
						params.objectType = 1;
					} else if (Config.siphoneregex.test(dest) || Config.extphoneregex.test(dest)) {
						params.objectType = 2;
					} else if (dest.indexOf("ivrNode") >= 0) {
						params.objectType = 3;
					} else {
						params.objectType = 0;
					}
					params.transferObject = dest;
					logreq("ClinkAgent.consult", params);
					ClinkAgent.transfer(params);
				}
			};

			softphone.senddtmf = function (dtmf) {
				var params = {};
				params.digits = dtmf;
				params.direction = "out";
				logreq("ClinkAgent.dtmf", params);
				ClinkAgent.dtmf(params);
			};

			softphone.listen = function (dn, agentid, callback) {
				var params = {};
				params.cno = agentid || dn;
				logreq("ClinkAgent.spy", params);
				ClinkAgent.spy(params);
			};

			softphone.bargein = function (dn, agentid, callback) {
				var params = {};
				params.cno = agentid || dn;
				logreq("ClinkAgent.barge", params);
				ClinkAgent.barge(params);
			};

			softphone.intercept = function (dn, agentid, callback) {
				return softphone.forcehangup(dn, agentid, callback);
			};

			softphone.forcehangup = function (dn, agentid, callback) {
				var params = {};
				params.cno = agentid || dn;
				logreq("ClinkAgent.disconnect", params);
				ClinkAgent.disconnect(params);
			};

			softphone.OnLinkConnected = function () {
				var evt = {};
				addJsonIntValue(evt, "event", MSGTYPE.EVT_LINKCONNCTED);
				addJsonStringValue(evt, "eventdesc", "EVT_LINKCONNECTED");
				softphone.OnCTIEvent(evt);
			};

			softphone.OnLinkDisconnected = function (event) {
				var evt = {};
				addJsonIntValue(evt, "event", MSGTYPE.EVT_LINKDISCONNCTED);
				addJsonStringValue(evt, "eventdesc", "EVT_LINKDISCONNCTED");
				softphone.OnCTIEvent(evt);

				try {
					const jsonString = JSON.stringify(event); // 将obj转换为字符串形式
					const clonedObj = JSON.parse(jsonString); // 将字符串重新转换为对象
					console.log('检测语音平台通信中断消息', clonedObj);
					$E.getActiveProject().events.notify('OnLinkDisconnected', clonedObj);
				} catch (err) {
					console.warn("检测语音平台通信中断消息-通知监听异常(可忽略)-" + err.message);
				}
			};

			softphone.OnRingback = function (event) {
				//eventcallback evt_dialing
				logmsg("此处应发送ringback消息", event);
			};

			softphone.OnReleased = function (event) {
				var servertime;
				var evt = {};
				if (this.callinfo.activecall != null) {
					servertime = $SC.getservertime();
					addJsonIntValue(evt, "event", MSGTYPE.EVT_RELEASED);
					addJsonStringValue(evt, "eventdesc", "EVT_RELEASED");
					addJsonDateValue(evt, "servertime", servertime);
					addJsonStringValue(evt, "dn", softphone.dn);
					addJsonStringValue(evt, "callid", this.callinfo.activecall.connectionid);
					addJsonStringValue(evt, "channelid", softphone.channelid);
					this.callinfo.activecall = null;
					softphone.OnCTIEvent(evt);
				} else if (this.callinfo.heldcall != null) {
					servertime = $SC.getservertime();
					addJsonIntValue(evt, "event", MSGTYPE.EVT_RELEASED);
					addJsonStringValue(evt, "eventdesc", "EVT_RELEASED");
					addJsonDateValue(evt, "servertime", servertime);
					addJsonStringValue(evt, "dn", softphone.dn);
					addJsonStringValue(evt, "callid", this.callinfo.heldcall.connectionid);
					addJsonStringValue(evt, "channelid", softphone.channelid);
					this.callinfo.heldcall = null;
					softphone.OnCTIEvent(evt);
				} else {
					return;
				}
				if (this.callinfo.activecall == null && this.callinfo.heldcall == null) {
					this.callinfo.callendtime = servertime;
				}
			};

			softphone.OnEstablished = function (event) {
				if (this.callinfo.activecall != null) {
					var servertime = $SC.getservertime();
					this.callinfo.activecall.state = Connection.STATE_TALKING;
					if (this.callinfo.establishtime == undefined) {
						this.callinfo.establishtime = servertime;
					}

					var evt = {};
					addJsonIntValue(evt, "event", MSGTYPE.EVT_ESTABLISH);
					addJsonStringValue(evt, "eventdesc", "EVT_ESTABLISH");
					addJsonDateValue(evt, "servertime", servertime);
					addJsonStringValue(evt, "dn", softphone.dn);
					addJsonStringValue(evt, "channelid", softphone.channelid);

					var userdatas = {};
					var ucid = event.mainUniqueId;
					if (ucid) {
						//addJsonStringValue(evt, "ucid", ucid);
						addJsonStringValue(userdatas, "ucid", ucid);
						addJsonStringValue(userdatas, "mainUniqueId", ucid);
						ucid = event.uniqueId;
						if (ucid) {
							addJsonStringValue(userdatas, "UniqueId", ucid);
						}
						addJsonDatasValue(evt, "userdata", userdatas);
					};

					addJsonStringValue(evt, "callid", this.callinfo.activecall.connectionid);
					if (this.callinfo.heldcall) {
						addJsonStringValue(evt, "prevconnectionid", this.callinfo.heldcall.connectionid);
					}

					softphone.OnCTIEvent(evt);
				}
			}
			softphone.OnConferenced = function (event) {
				if (this.callinfo.heldcall != null && this.callinfo.activecall != null) {
					//console.log('此处发送EVT_CONFERENCED');


					var evt = {};

					addJsonIntValue(evt, "event", MSGTYPE.EVT_CONFERENCED);
					addJsonIntValue(evt, "eventdesc", "EVT_CONFERENCED");
					addJsonStringValue(evt, "dn", softphone.dn);
					addJsonStringValue(evt, "channelid", softphone.channelid);

					addJsonStringValue(evt, "callid", this.callinfo.activecall.connectionid);
					if (this.callinfo.heldcall) {
						addJsonStringValue(evt, "prevconnectionid", this.callinfo.heldcall.connectionid);
					}

					var addedParty;		//
					if (addedParty) {
						addJsonStringValue(evt, "addedparty", addedParty);
					}

					//this.callinfo.activecall = this.callinfo.heldcall;
					this.callinfo.activecall.state = Connection.STATE_TALKING;
					this.callinfo.heldcall = null;

					softphone.OnCTIEvent(evt);

				}
			};

			softphone.OnHeld = function (event) {
				if (this.callinfo.activecall != null) {
					this.callinfo.heldcall = this.callinfo.activecall;
					this.callinfo.heldcall.state = Connection.STATE_HELD;
					this.callinfo.activecall = null;

					var evt = {};

					addJsonIntValue(evt, "event", MSGTYPE.EVT_HELD);
					addJsonIntValue(evt, "eventdesc", "EVT_HELD");
					addJsonStringValue(evt, "dn", softphone.dn);
					addJsonStringValue(evt, "channelid", softphone.channelid);
					addJsonStringValue(evt, "holdingdevice", softphone.dn);
					addJsonStringValue(evt, "callid", this.callinfo.heldcall.connectionid);
					softphone.OnCTIEvent(evt);
				}
			};

			softphone.OnRetrieved = function (event) {
				if (this.callinfo.heldcall != null) {
					this.callinfo.activecall = this.callinfo.heldcall;
					this.callinfo.activecall.state = Connection.STATE_TALKING;
					this.callinfo.heldcall = null;

					var evt = {};

					addJsonIntValue(evt, "event", MSGTYPE.EVT_RETRIEVED);
					addJsonIntValue(evt, "eventdesc", "EVT_RETRIEVED");
					addJsonStringValue(evt, "dn", softphone.dn);
					addJsonStringValue(evt, "channelid", softphone.channelid);
					addJsonStringValue(evt, "callid", this.callinfo.activecall.connectionid);
					softphone.OnCTIEvent(evt);
				}
			};

			softphone.OnCTICallBack = function (operation, result) {
				logmsg("OnCTICallBack(" + operation + ")", result);
				if (result.code == 0) {
					console.log("cti操作", operation, "成功");
					if (operation == 'hold') {
						//保持没有回调消息，以这里成功为准
						var mevent = {};
						this.OnHeld(mevent);
					} else if (operation == 'unhold') {
						//接回没有回调消息，以这里成功为准
						var mevent = {};
						this.OnRetrieved(mevent);
					}
				} else {
					//eventcallback evt_error
					softphone.InternalOnError(operation + "failed: " + JSON.stringify(result));
				}
			}
			softphone.OnAllCallReleased = function () {
				var servertime;
				var evt = {};
				if (this.callinfo.activecall != null) {
					servertime = $SC.getservertime();
					addJsonIntValue(evt, "event", MSGTYPE.EVT_RELEASED);
					addJsonStringValue(evt, "eventdesc", "EVT_RELEASED");
					addJsonDateValue(evt, "servertime", servertime);
					addJsonStringValue(evt, "dn", softphone.dn);
					addJsonStringValue(evt, "callid", this.callinfo.activecall.connectionid);
					addJsonStringValue(evt, "channelid", softphone.channelid);
					this.callinfo.activecall = null;
					softphone.OnCTIEvent(evt);
				}
				if (this.callinfo.heldcall != null) {
					servertime = $SC.getservertime();
					addJsonIntValue(evt, "event", MSGTYPE.EVT_RELEASED);
					addJsonStringValue(evt, "eventdesc", "EVT_RELEASED");
					addJsonDateValue(evt, "servertime", servertime);
					addJsonStringValue(evt, "dn", softphone.dn);
					addJsonStringValue(evt, "callid", this.callinfo.heldcall.connectionid);
					addJsonStringValue(evt, "channelid", softphone.channelid);
					this.callinfo.heldcall = null;
					softphone.OnCTIEvent(evt);
				}
				if (servertime) {
					this.callinfo.callendtime = servertime;
				}
			};

			softphone.OnAgentLogin = function (event) {
				var evt = {};
				addJsonIntValue(evt, "event", MSGTYPE.EVT_AGENTLOGIN);
				addJsonStringValue(evt, "eventdesc", "EVT_AGENTLOGIN");
				addJsonIntValue(evt, "WorkMode", 3);
				addJsonStringValue(evt, "dn", softphone.dn);
				addJsonStringValue(evt, "channelid", softphone.channelid);
				softphone.OnCTIEvent(evt);
			};

			softphone.OnAgentLogout = function (event) {
				var evt = {};
				addJsonIntValue(evt, "event", MSGTYPE.EVT_AGENTLOGOUT);
				addJsonStringValue(evt, "eventdesc", "EVT_AGENTLOGOUT");
				addJsonIntValue(evt, "WorkMode", 0);
				addJsonStringValue(evt, "dn", softphone.dn);
				addJsonStringValue(evt, "channelid", softphone.channelid);
				softphone.OnCTIEvent(evt);
			};

			softphone.OnAgentNotReady = function (event) {
				var evt = {};
				addJsonIntValue(evt, "event", MSGTYPE.EVT_AGENTNOTREADY);
				addJsonStringValue(evt, "eventdesc", "EVT_AGENTNOTREADY");
				addJsonIntValue(evt, "WorkMode", 3);
				addJsonStringValue(evt, "dn", softphone.dn);
				addJsonStringValue(evt, "channelid", softphone.channelid);
				softphone.OnCTIEvent(evt);
			};

			softphone.OnAgentACW = function (event) {
				var evt = {};
				addJsonIntValue(evt, "event", MSGTYPE.EVT_AGENTNOTREADY);
				addJsonStringValue(evt, "eventdesc", "EVT_AGENTNOTREADY");
				addJsonIntValue(evt, "WorkMode", 4);
				addJsonStringValue(evt, "dn", softphone.dn);
				addJsonStringValue(evt, "channelid", softphone.channelid);
				softphone.OnCTIEvent(evt);
			};

			softphone.OnAgentReady = function (event) {
				var evt = {};
				addJsonIntValue(evt, "event", MSGTYPE.EVT_AGENTREADY);
				addJsonStringValue(evt, "eventdesc", "EVT_AGENTREADY");
				addJsonIntValue(evt, "workmode", 2);
				addJsonStringValue(evt, "dn", softphone.dn);
				addJsonStringValue(evt, "channelid", softphone.channelid);
				softphone.OnCTIEvent(evt);
			};

			softphone.OnRinging = function (event) {
				logmsg("OnRinging:", event);
				//转接来电，transferCno, 会议来电：consulterCno
				if (event.callType == 4 && !event.transferCno && !event.consulterCno) {
					//previewoutboundcall
					/*
					var m_evt = {
						evt:'dialing',
						ani: event.obClid,
						dnis:event.customerNumber
					};
					return softphone.OnDialing(m_evt);
					 */
					return softphone.OnDialing(event);
				} else {
					if (!this.callinfo.activecall && !this.callinfo.heldcall) {
						this.callinfo = CallInfo.createNew();
					}
					if (!this.callinfo.activecall) {
						this.callinfo.activecall = Connection.createNew();
						this.callinfo.starttime = $SC.getservertime();
						this.callinfo.activecall.state = Connection.STATE_RINGING;
						this.callinfo.activecall.connectionid = connectionid1;
						if (!this.callinfo.heldcall) {
							this.callinfo.ani = event.customerNumber;
							this.callinfo.dnis = event.hotline;
							this.callinfo.calltype = CallInfo.CALLTYPE_INBOUND;
						}
					}
					//eventcallback evt_ringing
					//event.variables里为随路数据，测试环境目前没有
					//event.qno为队列号 event.qname为队列名
					//event.ivrId为ivr号 event.ivrName为流程名

					var evt = {};

					addJsonIntValue(evt, "event", MSGTYPE.EVT_RINGING);
					addJsonIntValue(evt, "eventdesc", "EVT_RINGING");
					addJsonStringValue(evt, "dn", softphone.dn);
					addJsonStringValue(evt, "callid", this.callinfo.activecall.connectionid);
					addJsonStringValue(evt, "channelid", softphone.channelid);

					var dnis = event.obClid || event.dnis || event.transferCno;
					var ani = event.customerNumber || event.ani;
					addJsonStringValue(evt, "dnis", dnis);
					addJsonStringValue(evt, "ani", ani);

					var userdatas = {};
					var ucid = event.mainUniqueId;
					if (ucid) {
						//addJsonStringValue(evt, "ucid", ucid);
						addJsonStringValue(userdatas, "ucid", ucid);
						addJsonStringValue(userdatas, "mainUniqueId", ucid);
						ucid = event.uniqueId;
						if (ucid) {
							addJsonStringValue(userdatas, "UniqueId", ucid);
						}
					} else {
						logmsg("no mainUniqueId");
					}

					var qno = event.qno;
					if (qno) {
						addJsonStringValue(userdatas, "qno", qno);
					}

					var qname = event.qname;
					if (qname) {
						addJsonStringValue(userdatas, "qname", qname);
					}

					var customerProvince = event.customerProvince;
					if (customerProvince) {
						addJsonStringValue(userdatas, "customerProvince", customerProvince);
					}

					var customerCity = event.customerCity;
					if (customerCity) {
						addJsonStringValue(userdatas, "customerCity", customerCity);
					}
					var ivrId = event.ivrId;
					if (ivrId) {
						addJsonStringValue(userdatas, "ivrId", ivrId);
					}
					var ivrName = event.ivrName;
					if (ivrName) {
						addJsonStringValue(userdatas, "ivrName", ivrName);
					}

					addJsonDatasValue(evt, "userdata", userdatas);

					softphone.OnCTIEvent(evt);
				}
			};

			softphone.OnDialing = function (event) {
				var ani = event.obClid || event.ani;
				var dnis = event.customerNumber || event.dnis;
				if (!this.callinfo.activecall && !this.callinfo.heldcall) {
					this.callinfo = CallInfo.createNew();
				}
				if (!this.callinfo.activecall) {
					this.callinfo.activecall = Connection.createNew();
					this.callinfo.starttime = $SC.getservertime();
					this.callinfo.activecall.state = Connection.STATE_DIALING;
					this.callinfo.activecall.connectionid = connectionid1;
					if (!this.callinfo.heldcall) {
						this.callinfo.ani = ani || this.dn;
						this.callinfo.dnis = dnis;
						this.callinfo.calltype = CallInfo.CALLTYPE_OUTBOUND;
					} else {
						if (this.callinfo.heldcall.connectionid = connectionid1) {
							this.callinfo.activecall.connectionid = connectionid2;
						}
					}
				}

				var evt = {};

				addJsonIntValue(evt, "event", MSGTYPE.EVT_DIALING);
				addJsonIntValue(evt, "eventdesc", "EVT_DIALING");
				addJsonStringValue(evt, "dn", softphone.dn);
				addJsonStringValue(evt, "callid", this.callinfo.activecall.connectionid);
				addJsonStringValue(evt, "channelid", softphone.channelid);

				addJsonStringValue(evt, "dnis", dnis);
				addJsonStringValue(evt, "ani", ani);

				var userdatas = {};
				var ucid = event.mainUniqueId;
				if (ucid) {
					//addJsonStringValue(evt, "ucid", ucid);
					addJsonStringValue(userdatas, "ucid", ucid);
					addJsonStringValue(userdatas, "mainUniqueId", ucid);
					ucid = event.uniqueId;
					if (ucid) {
						addJsonStringValue(userdatas, "UniqueId", ucid);
					}
					addJsonDatasValue(evt, "userdata", userdatas);
				} else {
					logmsg("no mainUniqueId");
				}

				if (this.callinfo.heldcall) {
					addJsonStringValue(evt, "prevconnectionid", this.callinfo.heldcall.connectionid);
				}
				softphone.OnCTIEvent(evt);
			};

			softphone.OnPreviewOutCall = function (event) {
				logmsg("OnPreviewOutCall:", event);
			};

			softphone.OnPreviewOutCallStart = function (event) {
				logmsg("OnPreviewCallStart:", event);
			}
			softphone.OnPreviewOutCallRinging = function (event) {
				logmsg("OnPreviewOutCallRinging:", event);
				//eventcallback evt_ringback
				console.log('此处发送EVT_RINGBACK');
			};

			softphone.OnPreviewOutCallBridge = function (event) {
				logmsg("OnPreviewOutCallBridge:", event);
				this.OnEstablished(event);
			};

			softphone.OnConsultStart = function (event) {
				logmsg("OnConsultStart:", event);
				this.OnHeld(event);
				this.OnDialing(event);
			};

			softphone.OnConsultLink = function (event) {
				logmsg("OnConsultLink:", event);
				this.OnEstablished(event);
			};

			softphone.OnConsultError = function (event) {
				logmsg("OnConsultError:", event);
				this.OnReleased(event);
				this.OnRetrieved(event);
			};

			softphone.OnUnconsult = function (event) {
				logmsg("OnUnconsult:", event);
				if (this.callinfo.activecall != null) {
					//eventcallback evt_ringback
					//console.log('此处发送EVT_RELEASE，call '+this.callinfo.activecall.callid+" released");
					//this.callinfo.activecall=null;
					this.OnReleased(event);
				}
				this.OnRetrieved();
			};

			softphone.OnConsultThreeway = function (event) {
				logmsg("OnConsultThreeway:", event);
				this.OnConferenced(event);
			};

			softphone.OnConsultThreewayUnlink = function (event) {
				logmsg("OnConsultThreewayUnlink:", event);
			};

			softphone.OnConsultTransfer = function (event) {
				logmsg("OnConsultTransfer:", event);
				//EVT_PARTYCHANGED if needed
				////this.OnAllCallReleased(event);
			};

			softphone.OnKickout = function (event) {
				logmsg("OnKickout:", event);
				var evt = {};
				addJsonIntValue(evt, "event", MSGTYPE.EVT_AGENTLOGOUT);
				addJsonStringValue(evt, "eventdesc", "EVT_AGENTLOGOUT");
				addJsonStringValue(evt, "logoutex", "Kickout");
				addJsonStringValue(evt, "dn", softphone.dn);
				addJsonStringValue(evt, "channelid", softphone.channelid);
				softphone.OnCTIEvent(evt);
			};

			softphone.OnBreakLine = function (event) {
				logmsg("OnBreakLine:", event);
				return softphone.OnLinkDisconnected(event);
			}

			softphone.OnAgentStatusEvent = function (event) {
				logmsg("OnAgentStatusEvent:", event);
				var evt = {};

				if (event.deviceStatus == 0) {
					//此处可以认为所有的电话已经释放
					this.OnAllCallReleased();
				}
				//if (event.code === "BUSY" && (event.action === "busyIb" || event.action === "busyTransfer" || event.action === "busyAgentOb")) {
				if (event.code === "BUSY" && (event.action === "busyIb" || event.action === "busyTransfer" || event.action === "busyConsult" || event.action === "busyOb")) {
					this.OnEstablished(event);
				} else if (event.code == 'PAUSE') {
					//var reasoncode=Config.reasoncodearray.findIndex(function(e,index){
					//	if(e==event.pauseDescription)
					//		return index;
					//})
					var reasoncode = arrayfind(softphone.auxcodes, event.pauseDescription, 1, 0);
					//eventcallback evt_agentnotready aux
					addJsonIntValue(evt, "event", MSGTYPE.EVT_AGENTNOTREADY);
					addJsonStringValue(evt, "eventdesc", "EVT_AGENTNOTREADY");
					if (reasoncode) {
						addJsonIntValue(evt, "reasoncode", reasoncode);
					}
					addJsonIntValue(evt, "workmode", 3);
					addJsonStringValue(evt, "dn", softphone.dn);
					addJsonStringValue(evt, "channelid", softphone.channelid);
					softphone.OnCTIEvent(evt);
				} else if (event.code == 'OFFLINE') {
					//eventcallback evt_agentlogout
					var evt = {};
					addJsonIntValue(evt, "event", MSGTYPE.EVT_AGENTLOGOUT);
					addJsonStringValue(evt, "eventdesc", "EVT_AGENTLOGOUT");
					addJsonStringValue(evt, "dn", softphone.dn);
					addJsonStringValue(evt, "channelid", softphone.channelid);
					softphone.OnCTIEvent(evt);
				} else if (event.code == 'IDLE') {
					//eventcallback evt_agentready
					addJsonIntValue(evt, "event", MSGTYPE.EVT_AGENTREADY);
					addJsonStringValue(evt, "eventdesc", "EVT_AGENTREADY");
					addJsonIntValue(evt, "workmode", 2);
					addJsonStringValue(evt, "dn", softphone.dn);
					addJsonStringValue(evt, "channelid", softphone.channelid);
					softphone.OnCTIEvent(evt);
				} else if (event.code == 'WRAPUP') {
					if (TRClink2Config.acwreasoncode > 0) {
						this.aux(TRClink2Config.acwreasoncode);
					} else {
						//将wrapup状态推迟为最大时长 10分钟
						var params = {};
						params.wrapupTime = 599;
						ClinkAgent.prolongWrapup(params);
					}
					//eventcallback evt_agentnotready acw
					addJsonIntValue(evt, "event", MSGTYPE.EVT_AGENTNOTREADY);
					addJsonStringValue(evt, "eventdesc", "EVT_AGENTNOTREADY");
					addJsonIntValue(evt, "workmode", 4);
					addJsonStringValue(evt, "dn", softphone.dn);
					addJsonStringValue(evt, "channelid", softphone.channelid);
					softphone.OnCTIEvent(evt);
				}
			}

			softphone.OnCTICallBack = function (operation, result) {
				if (result.code == 0) {
					console.log("cti操作", operation, "成功");
					if (operation == 'hold') {
						//保持没有回调消息，以这里成功为准
						var evt = {};
						this.OnHeld(evt);
					} else if (operation == 'unhold') {
						//接回没有回调消息，以这里成功为准
						var evt = {};
						this.OnRetrieved(evt);
					}
				} else {
					//eventcallback evt_error
					//console.log("cti操作",operation,"失败，此处应发送错误消息");
					softphone.InternalOnError(result.msg || "cti操作失败" + "，请求：" + result.reqType);
				}
			};

			return softphone;
		}
	};

	return ELITETRClink2.createNew();
}));